package Domain;

import org.apache.log4j.Logger;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;

public class HashBasedRecommendationSet implements IRecommendationSet, Serializable {
    private HashMap<String, HashMap<String, Recommendation>> m_set;
    static Logger logger = Logger.getLogger("MovieSystem.Domain.HashBasedRecommendationSet");

    public HashBasedRecommendationSet(){
        m_set = new HashMap<String, HashMap<String, Recommendation>>();
    }

    public Recommendation get(String user, String item) {
        Recommendation ans = null;

        if(m_set.containsKey(item))
            if(m_set.get(item).containsKey(user))
                ans = m_set.get(item).get(user);

        return ans;
    }

    public void add(Recommendation r) {
        String movieId = r.getMovieName();
        String userId = r.getUserName();
        if (m_set.containsKey(movieId)) {
            logger.debug("recommendation for "+movieId+ " by user "+userId+", rank = "+r.getUserRank());
            m_set.get(movieId).put(userId,r);
        }
        else {
            logger.info("First recommendation for the movie: "+movieId);
            HashMap<String,Recommendation> movieRecs = new HashMap<String,Recommendation>();
            movieRecs.put(userId,r);
            m_set.put(movieId,movieRecs);
        }
    }

    public void remove(Recommendation r) {
        String movieId = r.getMovieName();
        String userId = r.getUserName();
        if(m_set.containsKey(movieId))
            m_set.get(movieId).remove(userId);
        else
            logger.info("No recomendation for movie: "+movieId+". ignoring comnand.");                    

    }

    public double getRank(String user, String movie){
        double ans = 0;

        Recommendation r = get(user, movie);
        if(r != null){
            ans = r.getUserRank();
        }
        else {
            logger.info("Get rank ("+user+","+movie+") does not exist. assuming 0.");
        }

        return ans;
    }

    public boolean hasRank(String user, String movie){
        return (m_set.containsKey(movie) && m_set.get(movie).containsKey(user));
    }

    public double getAvgRank(String user) {
        double ans = 0;      

        Collection<Recommendation> userItems = getUserItems(user);
        for(Recommendation r : userItems){
            ans += r.getUserRank();           
        }
        ans = ans / userItems.size();

        logger.debug("Average rank ("+user+") = "+ans);
        return ans;
    }

    public Collection<Recommendation> getUserItems(String user) {
        LinkedList<Recommendation> ans = new LinkedList<Recommendation>();

        for(HashMap<String, Recommendation> userMap : m_set.values()){
            if(userMap.containsKey(user)){
                ans.add(userMap.get(user));
            }
        }

        logger.debug("Recommendation of user amount ("+user+") = "+ans.size());
        return ans;
    }

    public Collection<Recommendation> getMovieItems(String movie) {
        LinkedList<Recommendation> ans = new LinkedList<Recommendation>();
        HashMap<String, Recommendation> recommendingUsers = m_set.get(movie);

        if (recommendingUsers != null){
            for(Recommendation recommendation : recommendingUsers.values())
                ans.add(recommendation);
            logger.debug("Recommendation of movie amount ("+movie+") = "+ans.size());
        }
        else
            logger.debug("No recomendation for movie: "+movie);

        return ans;
    }

    public Collection<String> getJointItems(String user1, String user2) {
        LinkedList<String> ans = new LinkedList<String>();

        for(HashMap<String, Recommendation> userMap : m_set.values()){
            if(userMap.containsKey(user1) && userMap.containsKey(user2)){
                ans.add(userMap.get(user1).getMovieName());
            }
        }

        logger.debug("Joint movies amount ("+user1+","+user2+") = "+ans.size());
        return ans;
    }

    public Collection<Recommendation> values(){
        LinkedList<Recommendation> ans = new LinkedList<Recommendation>();

        for(HashMap<String, Recommendation> userMap : m_set.values()){
            ans.addAll(userMap.values());
        }

        return ans;
    }

    public Collection<String> getUsers(){
        HashMap<String, Recommendation> ans = new HashMap<String, Recommendation>();

        for(HashMap<String, Recommendation> userMap : m_set.values()){
            ans.putAll(userMap);
        }

        logger.debug("Users amount = "+ans.keySet().size());
        return ans.keySet();        
    }

    public Collection<String> getRecommendingUsers(String movie){
        LinkedList<String> ans = new LinkedList<String>();

        if(m_set.containsKey(movie))
            ans.addAll(m_set.get(movie).keySet());

        logger.debug("Recommending users amount ("+movie+") = "+ans.size());
        return ans;
    }

    // AVG
    public double getMovieRank(String movie){
        double ans = 0;

        Collection<Recommendation> movieItems = getMovieItems(movie);
        for(Recommendation r : movieItems){
            ans += r.getUserRank();           
        }
        ans = ans / movieItems.size();

        logger.debug("Average rank ("+movie+") = "+ans);
        return ans;
    }
}
